<?php

/*
 * This file is part of the php-phantomjs.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace lemon\phantomjs;

use lemon\phantomjs\Procedure\ProcedureLoaderInterface;
use lemon\phantomjs\Procedure\ProcedureCompilerInterface;
use lemon\phantomjs\Http\MessageFactoryInterface;
use lemon\phantomjs\Http\RequestInterface;
use lemon\phantomjs\Http\ResponseInterface;
use lemon\phantomjs\DependencyInjection\ServiceContainer;

/**
 * PHP PhantomJs
 *
 * @author Jon Wenmoth <contact@jonnyw.me>
 */
class Client implements ClientInterface
{
	/**
	 * Client.
	 *
	 * @var \lemon\phantomjs\ClientInterface
	 * @access private
	 */
	private static $instance;

	/**
	 * PhantomJs engine.
	 *
	 * @var \lemon\phantomjs\Engine
	 * @access protected
	 */
	protected $engine;

	/**
	 * Procedure loader.
	 *
	 * @var \lemon\phantomjs\Procedure\ProcedureLoaderInterface
	 * @access protected
	 */
	protected $procedureLoader;

	/**
	 * Procedure validator.
	 *
	 * @var \lemon\phantomjs\Procedure\ProcedureCompilerInterface
	 * @access protected
	 */
	protected $procedureCompiler;

	/**
	 * Message factory.
	 *
	 * @var \lemon\phantomjs\Http\MessageFactoryInterface
	 * @access protected
	 */
	protected $messageFactory;

	/**
	 * Procedure template
	 *
	 * @var string
	 * @access protected
	 */
	protected $procedure;

	/**
	 * Internal constructor
	 *
	 * @access public
	 * @param  \lemon\phantomjs\Engine							   $engine
	 * @param  \lemon\phantomjs\Procedure\ProcedureLoaderInterface   $procedureLoader
	 * @param  \lemon\phantomjs\Procedure\ProcedureCompilerInterface $procedureCompiler
	 * @param  \lemon\phantomjs\Http\MessageFactoryInterface		 $messageFactory
	 * @return void
	 */
	public function __construct(Engine $engine, ProcedureLoaderInterface $procedureLoader, ProcedureCompilerInterface $procedureCompiler, MessageFactoryInterface $messageFactory){
		$this->engine			= $engine;
		$this->procedureLoader   = $procedureLoader;
		$this->procedureCompiler = $procedureCompiler;
		$this->messageFactory	= $messageFactory;
		$this->procedure		 = 'http_default';
	}

	/**
	 * Get singleton instance
	 *
	 * @access public
	 * @return \lemon\phantomjs\Client
	 */
	public static function getInstance(){
		if (!self::$instance instanceof ClientInterface) {

			$serviceContainer = ServiceContainer::getInstance();

			self::$instance = new static(
				$serviceContainer->get('engine'),
				$serviceContainer->get('procedure_loader'),
				$serviceContainer->get('procedure_compiler'),
				$serviceContainer->get('message_factory')
			);
		}

		return self::$instance;
	}

	/**
	 * Get PhantomJs engine.
	 *
	 * @access public
	 * @return \lemon\phantomjs\Engine
	 */
	public function getEngine(){
		return $this->engine;
	}

	/**
	 * Get message factory instance
	 *
	 * @access public
	 * @return \lemon\phantomjs\Http\MessageFactoryInterface
	 */
	public function getMessageFactory(){
		return $this->messageFactory;
	}

	/**
	 * Get procedure loader instance
	 *
	 * @access public
	 * @return \lemon\phantomjs\Procedure\ProcedureLoaderInterface
	 */
	public function getProcedureLoader(){
		return $this->procedureLoader;
	}

	/**
	 * Send request
	 *
	 * @access public
	 * @param  \lemon\phantomjs\Http\RequestInterface  $request
	 * @param  \lemon\phantomjs\Http\ResponseInterface $response
	 * @return \lemon\phantomjs\Http\ResponseInterface
	 */
	public function send(RequestInterface $request, ResponseInterface $response){
		$procedure = $this->procedureLoader->load($this->procedure);

		$this->procedureCompiler->compile($procedure, $request);

		$procedure->run($request, $response);

		return $response;
	}

	/**
	 * Get log.
	 *
	 * @access public
	 * @return string
	 */
	public function getLog(){
		return $this->getEngine()->getLog();
	}

	/**
	 * Set procedure template.
	 *
	 * @access public
	 * @param  string $procedure
	 * @return void
	 */
	public function setProcedure($procedure){
		$this->procedure = $procedure;
	}

	/**
	 * Get procedure template.
	 *
	 * @access public
	 * @return string
	 */
	public function getProcedure(){
		return $this->procedure;
	}

	/**
	 * Get procedure compiler.
	 *
	 * @access public
	 * @return \lemon\phantomjs\Procedure\ProcedureCompilerInterface
	 */
	public function getProcedureCompiler(){
		return $this->procedureCompiler;
	}

	/**
	 * Set lazy request flag.
	 *
	 * @access public
	 * @return void
	 */
	public function isLazy(){
		$this->procedure = 'http_lazy';
	}
}
